<?php
/**
 * @see Epic_Simplexml_Element
 */
require 'Epic/Simplexml/Element.php';

/**
 * @category   Epic
 * @package    Epic_Simplexml
 * @author     Epic Core Team <nguyendnhat@gmail.com>
 */
class Epic_Simplexml_Layout
{	
	/**
     * Configuration xml
     * @var Epic_Simplexml_Element
     */
    private $_xml = null;
    
    /**
     * Blocks registry
     * @var array
     */
    private $_blocks = array();
    
    /**
     * Constructor
     * 
     * @param Epic_Controller_Request_Abstract $request
     */
    public function __construct(array $requestData) 
    {
    	$this->_loadString($requestData);
    }
    
    /**
     * Get block object by name
     *
     * @param string $name
     * @return Epic_View_Block
     */
    public function getBlock($name)
    {
        if (isset($this->_blocks[$name])) {
            return $this->_blocks[$name];
        } else {
            return false;
        }
    }
    
    /**
     * Imports XML string
     */
    private function _loadString($requestData)
    {
    	$enableCacheLayoutxml = Epic::getRegistry()->get('config')->getConfig('cache/cache_layoutxml');
    	// Handler cache layout xml
    	if ($enableCacheLayoutxml) {
    		$layoutxmlFile = file_get_contents(CACHE_DIR . 'layoutxml' . DS . $requestData['moduleName'] . '.xml');
    		$this->_xml = simplexml_load_string($layoutxmlFile, 'Epic_Simplexml_Element');
    	} else {
	    	$layoutxmlFile = file_get_contents(LAYOUT_DIR . $requestData['moduleName'] . '.xml');	
	        $this->_processLayoutXml(simplexml_load_string($layoutxmlFile, 'Epic_Simplexml_Element'));
	        // save file to cache with xml support
	        $this->_xml->asXML(CACHE_DIR . "layoutxml" . DS . $requestData['moduleName'] . '.xml');
    	}

        // find action correspond
        $xpath = $requestData['moduleName'] . '_' 
               . $requestData['controllerName'] . '_' 
               . $requestData['actionName'];
        $this->_xml = $this->_xml->getXpath($xpath);
    }
    
    private function _processLayoutXml($xml) {
    	//merge layout
    	$layoutXml = $xml;
    	$coreXml = simplexml_load_file(LAYOUT_DIR . "core.xml", 'Epic_Simplexml_Element');
    	foreach ($coreXml->children() as $childNode)
    		$layoutXml->appendChild($childNode);
    	
    	// apply layout for each action
    	$actionNodes = $layoutXml->xpath('/layout/child::*');
    	if (!is_null($actionNodes)) {
    		foreach ($actionNodes as $actionNode) {
    			if ((string)$actionNode->getName() == 'default')
    				continue;
    			// render layout for action
    			if (!is_null($actionNode['layout'])) {
    				$nameLayout = (string)$actionNode['layout'];
    			} else {
    				$defaultNode = $layoutXml->getXpath('/layout/default');
    				$nameLayout = (string)$defaultNode['layout'];
    			}
    			$layoutNode = $layoutXml->getXpath("/layout/default[@name='{$nameLayout}']")->children();
    			$layoutXml->getXpath("/layout/{$actionNode->getName()}")->appendChild($layoutNode);
    		}
    	
    		// remove layout node after used
    		$defaultNodes = $layoutXml->xpath('/layout/default');
    		foreach ($defaultNodes as $defaultNode) {
    			$layoutXml->removeChild($defaultNode);
    		}
    	}
    	
    	// process reference for each action
    	$referenceNodes = $layoutXml->xpath("//reference");
    	foreach ($referenceNodes as $referenceNode) {
    		$nameBlockReference = (string)$referenceNode['name'];
    		//$actionNode = $referenceNode->getParent();
    		//$nameAction = (string)$actionNode->getName();
    	
    		$referenceBlook = $actionNode->getXpath("//block[@name='{$nameBlockReference}']");
    	
    		$layoutXml->getXpath("//block[@name='{$nameBlockReference}']")->getParent()->appendChild($referenceNode->children());
    		$layoutXml->removeChild($referenceBlook);
    		$layoutXml->removeChild($referenceNode);
    	}
    	$this->_xml = $layoutXml;
    }
    
    /*
     * Generate block layout
     *
     * @return Epic_View_Block
     */
    public function generateLayout() 
    {
		$this->_processBlocks($this->_xml);
    	return $this->_blocks['layout'];
    }

    /*
     * Process generate block layout (add more in future)
     * 
     * @param Epic_Simplexml_Element $node
     */
 	private function _processBlocks($node)
    {
        $parentName = (string)$node['name'];
        foreach ($node as $children) {
            switch ($children->getName()) {
                case 'block':
                    $this->_generateBlock($children, $parentName);
                    $this->_processBlocks($children);
                    break;
            }
        }
    }
    
    /*
     * Generate block
     * 
     * @param Epic_Simplexml_Element $node
     * @param string $parentName
     */
	protected function _generateBlock($node, $parentName)
    {
        $block = $this->_createBlock($node);
        
        $parentBlock = $this->getBlock($parentName);
        if($parentBlock) {
			if(isset($node['var'])) {
				$var = (string)$node['var'];
				$parentBlock->$var = $block;
			} else 
        		$parentBlock->addView($block);
        }
    }

    /**
     * Create block
     * 
     * @param string $className
     * @param string $blockName
     * @param string $template
     * @return Epic_View_Block
     */
	private function _createBlock($node)
    {
    	$className = (string)$node['class'];
    	$blockName = (string)$node['name'];
    	
    	$enableCacheBlock = Epic::getRegistry()->get('config')->getConfig('cache/cache_block');
    	// enable cache
    	if ((string) $node['cache'] && $enableCacheBlock) {
    		$expire = (int)$node['expire'];
    		if(!$expire) {
    			throw new Epic_Simplexml_Exception('Expire is miss in layout xml.');
    		}
    	
    		$cache = new Epic_Cache_Handler('CONTENT');
    		$cache->setCacheDir(CACHE_DIR . 'block')
    		      ->setExpireTime($expire);

    		$templateCache = str_replace('/', '_', (string) $node['template']);
    		// create cache file if not exists or expire
    		if (!$cache->exists($templateCache)) {
    			$block = new $className(TEMPLATE_DIR . (string) $node['template']);
    			$cache->set($templateCache, $block->render());
    		}

    		// retrieve a template from cache instead of
    		$template = CACHE_DIR . 'block' . DS . $templateCache;
    	} else {
    		$template = TEMPLATE_DIR . (string) $node['template'];
    	}
    	
    	$block = new $className($template);
    	$this->_blocks[$blockName] = $block;
    	
        return $this->_blocks[$blockName];
    }
}